#include <optional>
#include <cassert>
#include <nice2d/util.h>
#include <nice2d/tilemap.h>
#include <nice2d/piece/piece.h>
#include <nice2d/piece/actions/piecemoveaction.h>

class PieceMoveActionData
{
public:
    std::shared_ptr<PieceScene> pieceScene;
    sf::Vector2i oldTilePosition;
    sf::Vector2f oldPosition;
    std::optional<sf::Vector2i> targetTileDestination;
    sf::Vector2f targetPosition;
    std::optional<sf::Vector2f> velocity;
};

PieceMoveAction::PieceMoveAction(std::shared_ptr<PieceScene> scene,const sf::Vector2i &position,float duration):
    FiniteTimeAction(duration),
    data(new PieceMoveActionData())
{
    data->pieceScene = scene;
    data->oldTilePosition = position;

    auto tile = data->pieceScene->getTileMap()->getTileByIndex(data->oldTilePosition);
    data->oldPosition = tile->getPosition();
}

PieceMoveAction::~PieceMoveAction()
{
}

void PieceMoveAction::setTargetTile(const sf::Vector2i& index)
{
    data->targetTileDestination = index;

    auto tile = data->pieceScene->getTileMap()->getTileByIndex(index);
    data->targetPosition = tile->getPosition();
}

sf::Vector2i PieceMoveAction::getOldPosition() const
{
    return data->oldTilePosition;
}

void PieceMoveAction::update(float deltaTime)
{
    if (getOwner().expired())
        return;

    auto piece = std::dynamic_pointer_cast<Piece>(getOwner().lock());
    assert(piece);

    if (!data->velocity.has_value()) {
        assert(data->targetTileDestination.has_value());
        data->velocity = (data->targetPosition - data->oldPosition) / getDuration();
    }

    auto pieceBox = piece->getBoundingBox();
    auto pieceOldCenter = getRectCenter(pieceBox);

    auto delta = data->velocity.value() * deltaTime;
    piece->move(delta.x, delta.y);

    pieceBox = piece->getBoundingBox();
    auto pieceCenter = getRectCenter(pieceBox);

    float oldDistance2 = distance2(pieceOldCenter,data->targetPosition);
    float currentDistance2 = distance2(pieceCenter,data->targetPosition);
    if (currentDistance2 >= oldDistance2) {
        auto targetTile = data->targetTileDestination.value();
        data->pieceScene->movePiece(piece,targetTile.x,targetTile.y);
        piece->setCenter(data->targetPosition);
        stop();
    }
}
